#!/usr/bin/env python
# $Id: trapd.py df54dec975f4 2012/09/09 20:22:40 pgurumur $
# Copyright (c) 2009 Prabhu Gurumurthy  <pgurumur@gmail.com>
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

__version__ = "$Revision: df54dec975f4 $"
__author__ = "$Author: pgurumur $"

# system imports
import re
import os
import optparse
import signal
import sys
import SocketServer

# local imports
# import cmdparser library to handle command line arguments
from venya.lib import cmdparser

# Import the core of the ssh implementation using paramiko
from venya.lib import sql
from venya.lib import log
from venya.lib import stdlib
from venya.lib import db4


class TrapHandler(SocketServer.StreamRequestHandler):
   def handle(self):
      request = self.rfile.readline()
      if request:
         self.server.log("request %s: %s",
               self.connection.getpeername(), request.rstrip())
         try:
            self.process(request)
         except Exception as err:
            self.server.log("exception: %s" %err.message)
      else:
         self.server.log("unable to read request!")

   def process(self, request):
      words = request.split()
      if len(words):
         command = words[0]
         print command

class TrapServer(SocketServer.UnixStreamServer):
   def __init__(self, address):
      self._address = None
      self._stopped = True
      self.log = log.StreamLog(stdlib.my_name(self))
      if self._address:
         if os.path.exists(self._address):
            try:
               os.unlink(self._address)
            except OSError as err:
               pass

         super(TrapServer, self).__init__(address, TrapHandler)

   def serve(self):
      self.serve_forever()



# Main client class, which imports the command parser class, the actual
# ssh handling is done using the handle function
# Main SSH client class
class trapd(cmdparser.GetOpt):
   option_list = (
         optparse.make_option("-a", "--add", type = "str",
            help = "Add entry to berkeley database", dest = "add"),
         optparse.make_option("-m", "--modify", type = "str",
            help = "Modify entry in berkeley database", dest = "modify"),
         optparse.make_option("-r", "--remove", type = "str",
            help = "Remove entry from berkeley database", dest = "remove"),
         optparse.make_option("-d", "--database", type = "str",
            help = "Configuration berkeley database",
            default = "trapd.db", dest = "database"),
         optparse.make_option("-s", "--server", action = "store_true",
            help = "Run server to connect to netdot db", default = False,
            dest = "server"),
         optparse.make_option("-h", "--host", type = "str",
            help = "Host that is sending the traps", dest = "host"),
         optparse.make_option("-t", "--trap", type = "str",
            help = "Type of SNMP trap", dest = "trap"),
         ) + cmdparser.GetOpt.option_list

   help = """

   SNMP Trap handling script, validates the trap received with netdot
   database, try to keep a persistent database connection

   Take 4 mandatory command line options and OID(s) as arguments
     host: Host that is generating the trap
     IP: IP address of the host that is generating the trap
     trap: Type of SNMP trap generated
     config: database configuration file

   arguments are list of command line arguments passed ideally after the command
   line options are given

   """

   def __init__(self, **keywords):
      super(self.__class__, self).__init__(**keywords)
      self._log = log.StreamLog(stdlib.my_name(self))
      self._db = None
      self._host = None
      self._dbopt = {
            1: "add",
            2: "modify",
            3: "remove",
            }

      self._bgpState = {
            1: 'idle',
            2: 'connect',
            3: 'active',
            4: 'opensent',
            5: 'openconfirm',
            6: 'established',
            }

   def bgptrap(self, varbinds):
      rebgp = re.compile(".1.3.6.1.2.1.15.3.1.2.((\d{1,3}\.){3}\d{1,3}):(\d+)")
      match = rebgp.match(varbinds[0])
      if match:
         (peer, octet, state) = match.groups()
         self._traphandle("bgp", peer, self._host, state)

   def close(self):
      if self._db:
         self._db.Close()

   def configdb(self, key, value, dboper):
      if not self._db:
         self.error("Cannot proceed without db handle!")

      if self._dbopt["dboper"] == "add":
         self._db[key] = value
      elif self._dbopt["dboper"] == "modify":
         if key in self._db:
            self._db[key] = value
         else:
            self.configdb(key, value, 1)
      elif self._dbopt["dboper"] == "remove":
         if key in self._db:
            del self._db[key]

   def handle(self):
      (options, args) = self.parse()
      if options.database:
         self._db = self._db.bdb(options.database)

      if options.server:

      if options.add or options.modify or options.remove:
         if len(args) < 2:
            dbvalue = args[1]
         else:
            self.error("too many values for database!")

         if options.add and options.modify:
            self.error("cannot use add and modify option together!")
         elif options.add and options.remove:
            self.error("cannot use add and remove option together!")
         elif options.remove and options.modify:
            self.error("cannot use remove and modify option together!")

         if options.add:
            self.configdb(options.add, dbvalue, 1)
         elif options.modify:
            self.configdb(options.modify, dbvalue, 2)
         elif options.remove:
            self.confgdb(options.modify, dbvalue, 3)
      else:
         varbinds = args[1:]

         if not options.host:
            self.error("Cannot proceed without a hostname!")
         else:
            self._host = options.host

         if not options.trap:
            self.error("Cannot proceed without trap type!")

         if len(varbinds):
            trap = options.trap.lower()
            if trap == "bgp":
               self.bgptrap(varbinds)
         else:
            self.error("Cannot proceed without varbinds!")
